package fr.umlv.jee.jawp.persistence.dao;

import java.util.List;

import javax.naming.Name;
import javax.naming.NamingException;

import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.NotFilter;

import fr.umlv.jee.jawp.persistence.object.Person;

public class PersonDAO implements IPersonDAO {

	private LdapTemplate ldapTemplate;
	private static final String BASE = "dc=jawp,dc=org";
	private String last_uid;

	public PersonDAO() {

		// TODO : use the right bean : LDAP_DAO.

		/* Context */
		LdapContextSource ldapContextSource = new LdapContextSource();
		ldapContextSource.setUrl("ldap://localhost:10389");
		ldapContextSource.setBase(BASE);

		try {
			ldapContextSource.afterPropertiesSet();
		} catch (Exception e) {

		}

		/* Template */
		LdapTemplate ldapTemplate = new LdapTemplate();
		ldapTemplate.setContextSource(ldapContextSource);
		setLdapTemplate(ldapTemplate);

	}

	@Override
	public void setLdapTemplate(LdapTemplate ldapTemplate) {
		this.ldapTemplate = ldapTemplate;
	}

	@Override
	public void create(final Person person) {
		DirContextAdapter context = new DirContextAdapter();

		/* UID */
		AndFilter filterAnd = new AndFilter();
		filterAnd.and(new EqualsFilter("objectclass", "dcObject"));
		filterAnd.and(new EqualsFilter("dc", "uid_save"));
		ldapTemplate.search("", filterAnd.encode(), new ContextMapper() {
			public Object mapFromContext(final Object context) {
				DirContextAdapter ctxAdapter = (DirContextAdapter) context;
				last_uid = (String) ctxAdapter.getObjectAttribute("uid");
				
				ctxAdapter.setAttributeValue("uid", String.valueOf(Integer.parseInt(last_uid) + 1));
				ctxAdapter.update();
				ldapTemplate.rebind(ctxAdapter.getDn(), ctxAdapter, null);
				return null;
			}
		});

		last_uid = String.valueOf(Integer.parseInt(last_uid) + 1);

		if (last_uid.length() == 1)
			person.setUid("00" + last_uid);
		else if (last_uid.length() == 2)
			person.setUid("0" + last_uid);
		else if (last_uid.length() == 3)
			person.setUid(last_uid);
		else
			person.setUid(last_uid);

		/*
		 * AndFilter filterAndGroup = new AndFilter(); filterAndGroup.and(new
		 * EqualsFilter("objectclass", "groupOfNames")); filterAndGroup.and(new
		 * EqualsFilter("member", person.getUser_id())); ldapTemplate.search("",
		 * filterAndGroup.encode(), new ContextMapper() { public Object
		 * mapFromContext(final Object context) { DirContextAdapter ctxAdapter =
		 * (DirContextAdapter) context; person.setGroup((String)
		 * ctxAdapter.getObjectAttribute("cn")); return null; } });
		 */

		mapToContext(person, context);

		ldapTemplate.bind(buildDn(person), context, null);

		/* Add the person into the group */
		AndFilter filter = new AndFilter();
		filter.and(new EqualsFilter("objectclass", "groupOfNames"));
		filter.and(new EqualsFilter("ou", person.getGroup()));

		ldapTemplate.search("", filter.encode(), new ContextMapper() {
			public Object mapFromContext(final Object context) {
				DirContextAdapter ctxAdapter = (DirContextAdapter) context;
				ctxAdapter.setAttributeValue("member", "cn=" + person.getUser_id()
						+ ",ou=people,dc=jawp,dc=org");
				ctxAdapter.update();
				ldapTemplate.rebind(ctxAdapter.getDn(), ctxAdapter, null);
				return null;
			}
		});
	}

	@Override
	public void update(Person person) {
		Name dn = buildDn(person);
		DirContextAdapter context = (DirContextAdapter) ldapTemplate.lookup(dn);
		mapToContext(person, context);
		ldapTemplate.modifyAttributes(dn, context.getModificationItems());
	}

	@Override
	public void delete(Person person) {
		ldapTemplate.unbind(buildDn(person));
	}

	@Override
	public Person findByPrimaryKeyMail(String user_id) {
		Name dn = buildDn(user_id);
		return (Person) ldapTemplate.lookup(dn, new PersonContextMapper());
	}

	@Override
	public List<?> findAll() {
		EqualsFilter filter = new EqualsFilter("objectclass", "person");
		return ldapTemplate.search(DistinguishedName.EMPTY_PATH, filter.encode(),
				getContextMapper());
	}

	@Override
	public List<?> findByName(String name) {
		EqualsFilter filter = new EqualsFilter("sn", name);
		return ldapTemplate.search(DistinguishedName.EMPTY_PATH, filter.encode(),
				getContextMapper());
	}

	@Override
	public List<?> findByGroup(String group) {
		EqualsFilter filter = new EqualsFilter("ou", group);

		/* We dont need to print the GROUP's label */
		NotFilter not = new NotFilter(new EqualsFilter("uid", "group"));
		AndFilter and = new AndFilter();
		and.append(filter);
		and.and(not);
		return ldapTemplate.search(DistinguishedName.EMPTY_PATH, and.encode(),
				getContextMapper());

	}

	@Override
	public List<?> findAGroup(String group) {
		EqualsFilter filter = new EqualsFilter("cn", group);
		return ldapTemplate.search(DistinguishedName.EMPTY_PATH, filter.encode(),
				getContextMapper());

	}

	protected ContextMapper getContextMapper() {
		return new PersonContextMapper();
	}

	protected Name buildDn(Person person) {
		return buildDn(person.getUser_id());
	}

	protected Name buildDn(String user_id) {
		DistinguishedName dn = new DistinguishedName();
		dn.add("ou", "people");
		dn.add("cn", user_id);
		return dn;
	}

	protected Name buildDnGroup(String group) {
		DistinguishedName dn = new DistinguishedName();
		dn.add("ou", "groups");
		dn.add("cn", "user");
		// dn.add("cn", group);

		return dn;
	}

	protected void mapToContext(Person person, DirContextAdapter context) {
		context.setAttributeValues("objectclass", new String[] { "top", "person",
				"organizationalPerson", "inetOrgPerson" });
		context.setAttributeValue("cn", person.getUser_id());
		context.setAttributeValue("sn", person.getLastName());
		context.setAttributeValue("description", person.getDescription());
		context.setAttributeValue("givenName", person.getFirstName());
		context.setAttributeValue("mobile", person.getMobile());
		context.setAttributeValue("postalAddress", person.getPostalAddress());
		context.setAttributeValue("postalCode", person.getPostalCode());
		context.setAttributeValue("preferredLanguage",
				person.getPreferredLanguage());
		context.setAttributeValue("street", person.getCity());
		context.setAttributeValue("telephoneNumber", person.getTelephoneNumber());
		context.setAttributeValue("uid", person.getUid());
		context.setAttributeValue("userPassword", person.getUserPassword());
	}

	protected void mapToContextGroup(Person person, DirContextAdapter context) {
		context.setAttributeValues("objectclass", new String[] { "top",
				"groupOfNames" });
		context.setAttributeValue("cn", person.getGroup());
	}

	private static class PersonContextMapper implements ContextMapper {
		public Object mapFromContext(Object ctx) {
			DirContextAdapter context = (DirContextAdapter) ctx;
			Person person = new Person();
			person.setUser_id(context.getStringAttribute("cn"));
			person.setLastName(context.getStringAttribute("sn"));
			person.setDescription(context.getStringAttribute("description"));
			person.setFirstName(context.getStringAttribute("givenName"));
			person.setMobile(context.getStringAttribute("mobile"));
			person.setPostalAddress(context.getStringAttribute("postalAddress"));
			person.setPostalCode(context.getStringAttribute("postalCode"));
			person.setPreferredLanguage(context
					.getStringAttribute("preferredLanguage"));
			person.setCity(context.getStringAttribute("street"));
			person.setTelephoneNumber(context.getStringAttribute("telephoneNumber"));
			person.setUid(context.getStringAttribute("uid"));
			person.setUserPassword(PersonDAO.byteArrayToHexString((byte[]) context
					.getObjectAttribute("userPassword")));
			return person;
		}
	}

	protected static String byteArrayToHexString(byte[] bArray) {

		// FIXME
		if (bArray == null)
			return null;

		StringBuffer buffer = new StringBuffer();

		for (byte b : bArray) {
			buffer.append(Integer.toHexString(b));
			buffer.append(" ");
		}

		return buffer.toString().toUpperCase();
	}

	public static void main(String[] args) {
		PersonDAO dao = new PersonDAO();

		dao.delete(new Person("toto", null, null, null, null, null, null, null,
				null, null, null, null, null));

		dao.create(new Person("toto", "toto", "toto", "user", "toto", "toto",
				"toto", "4578", "toto", "toto", "toto", "454546", "016465"));

	}
}
